/*
 * Copyright (c) 2022-2022 Huawei Technologies Co., Ltd. All rights reserved.
 * Copyright (c) 2022-2022 Huawei Device Co., Ltd. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 *    conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 *    of conditions and the following disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 *    to endorse or promote products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "los_credentials.h"
#include <errno.h>
#include "los_config.h"
#include "los_memory.h"
#include "los_user_container.h"

#ifdef LOSCFG_USER_CONTAINER

const int INIT_CREDENTIALS_NUM = 3;

Credentials g_rootCredentials = {
    .count = ATOMIC_INIT(INIT_CREDENTIALS_NUM),
    .uid = 0,
    .gid = 0,
    .euid = 0,
    .egid = 0,
    .userContainer = &g_rootUserContainer,
};

Credentials *PrepareCredential(LosProcessCB *runProcessCB)
{
    const Credentials *oldCredentials = runProcessCB->credentials;
    Credentials *newCredentials = NULL;

    UINT32 size = sizeof(Credentials);
    newCredentials = LOS_MemAlloc(m_aucSysMem1, size);

    if (newCredentials == NULL) {
        PRINT_ERR("PrepareCredential failed to malloc memory.\n");
        return NULL;
    }

    memcpy(newCredentials, oldCredentials, sizeof(Credentials));
    atomic_set(&newCredentials->count, 1);
    GetUserContainer(newCredentials->userContainer);

    return newCredentials;
}

INT32 CopyCredentials(unsigned long flags,  LosProcessCB *childProcessCB, LosProcessCB *runProcessCB)
{
    Credentials *newCredentials = NULL;
    UserContainer *newUserContainer = NULL;

    newCredentials = PrepareCredential(runProcessCB);
    if (!newCredentials) {
        return -LOS_ENOMEM;
    }

    if (flags & CLONE_NEWUSER) {
        newUserContainer = CreateUserContainer(newCredentials);
        if (newUserContainer == NULL) {
            goto error_put;
        }
        DerefUserContainer(newCredentials->userContainer);
        newCredentials->userContainer = newUserContainer;
    }

    childProcessCB->credentials = newCredentials;
    return 0;

error_put:
    DerefCredentials(newCredentials);
    return -LOS_ENOMEM;
}

void FreeCredentials(Credentials *credentials)
{
    if (credentials == NULL) {
        return;
    }

    if (credentials->userContainer) {
        DerefUserContainer(credentials->userContainer);
    }

    LOS_MemFree(m_aucSysMem1, credentials);   
}

void ExitProcessCredentials(LosProcessCB *processCB) 
{
    Credentials *pCredentials = NULL;
    pCredentials = processCB->credentials;

    DerefCredentials(pCredentials);
}

INT32 CommitCredentials(Credentials *newCredentials)
{
    Credentials *oldCredentials = OsCurrProcessGet()->credentials;

    if (atomic_read(&newCredentials->count) < 1) {
        return -ENOMEM;
    }

    OsCurrProcessGet()->credentials = newCredentials;
    DerefCredentials(oldCredentials);

    return 0;
}

INT32 UnShareUserContainer(unsigned long unshareFlags, LosProcessCB *runProcess)
{
    if (!(unshareFlags & CLONE_NEWUSER)) {
        return 0;
    }

    return CopyCredentials(unshareFlags, runProcess, runProcess);
}

INT32 ReplaceUserContainer(Credentials* credentials, UserContainer *targetUserContainer)
{
    if (credentials->userContainer) {
        DerefUserContainer(credentials->userContainer);
    }

    credentials->userContainer = GetUserContainer(targetUserContainer);
    return LOS_OK;
}

INT32 SetnsUserContainer(struct UserContainer *targetContainer, LosProcessCB *runProcess)
{
    Credentials *newCredentials = NULL;
    newCredentials = PrepareCredential(runProcess);
    if (newCredentials == NULL) {
        PRINT_ERR("SetnsUserContainer error, failed to copy original credentials");
        return -LOS_ENOMEM;
    }
    ReplaceUserContainer(newCredentials, (UserContainer *)targetContainer);
    CommitCredentials(newCredentials);
    return LOS_OK;
}
#endif